#include "singlefixedleg.h"
#include <ql/cashflows/fixedratecoupon.hpp>
#include <ql/cashflows/iborcoupon.hpp>
#include <ql/cashflows/cashflowvectors.hpp>
#include <ql/cashflows/cashflows.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/indexes/iborindex.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>

namespace QuantLib {

    SingleFixedLeg::SingleFixedLeg(
                     Type type,
                     Real nominal,
                     const Schedule& fixedSchedule,
                     Rate fixedRate,
                     const DayCounter& fixedDayCount)
		:Swap(1), 
		type_(type),
		nominal_(nominal),
		fixedSchedule_(fixedSchedule), 
		fixedRate_(fixedRate),
		fixedDayCount_(fixedDayCount)
	{

        paymentConvention_ = fixedSchedule_.businessDayConvention();

        legs_[0] = FixedRateLeg(fixedSchedule_)
            .withNotionals(nominal_)
            .withCouponRates(fixedRate_, fixedDayCount_)
            .withPaymentAdjustment(paymentConvention_);

        for (Leg::const_iterator i = legs_[0].begin(); i < legs_[0].end(); ++i)
            registerWith(*i);

        switch (type_) {
          case Payer:
            payer_[0] = -1.0;
            break;
          case Receiver:
            payer_[0] = +1.0;
            break;
          default:
            QL_FAIL("Unknown single-fixed-leg type");
        }
    }

    void SingleFixedLeg::setupArguments(PricingEngine::arguments* args) const {

        Swap::setupArguments(args);

        SingleFixedLeg::arguments* arguments = dynamic_cast<SingleFixedLeg::arguments*>(args);

        if (!arguments)  // it's a swap engine...
            return;

        arguments->type = type_;
        arguments->nominal = nominal_;

        const Leg& fixedCoupons = fixedLeg();

        arguments->fixedResetDates = arguments->fixedPayDates = std::vector<Date>(fixedCoupons.size());
        arguments->fixedCoupons = std::vector<Real>(fixedCoupons.size());

        for (Size i=0; i<fixedCoupons.size(); ++i) {
            boost::shared_ptr<FixedRateCoupon> coupon =
                boost::dynamic_pointer_cast<FixedRateCoupon>(fixedCoupons[i]);

            arguments->fixedPayDates[i] = coupon->date();
            arguments->fixedResetDates[i] = coupon->accrualStartDate();
            arguments->fixedCoupons[i] = coupon->amount();
        }
     
    }

    Rate SingleFixedLeg::fairRate() const {
        calculate();
        QL_REQUIRE(fairRate_ != Null<Rate>(), "result not available");
        return fairRate_;
    }

    Real SingleFixedLeg::fixedLegBPS() const {
        calculate();
        QL_REQUIRE(legBPS_[0] != Null<Real>(), "result not available");
        return legBPS_[0];
    }

    Real SingleFixedLeg::fixedLegNPV() const {
        calculate();
        QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available");
        return legNPV_[0];
    }

    void SingleFixedLeg::setupExpired() const {
        Swap::setupExpired();
        legBPS_[0] = 0.0;
        fairRate_ = Null<Rate>();
    }

    void SingleFixedLeg::fetchResults(const PricingEngine::results* r) const {
        static const Spread basisPoint = 1.0e-4;

        Swap::fetchResults(r);

        const SingleFixedLeg::results* results =
            dynamic_cast<const SingleFixedLeg::results*>(r);
        if (results) { // might be a swap engine, so no error is thrown
            fairRate_ = results->fairRate;
        } else {
            fairRate_ = Null<Rate>();
        }

        if (fairRate_ == Null<Rate>()) {
            // calculate it from other results
            if (legBPS_[0] != Null<Real>())
                fairRate_ = fixedRate_ - NPV_/(legBPS_[0]/basisPoint);
        }
    }

    void SingleFixedLeg::arguments::validate() const {
        Swap::arguments::validate();
        QL_REQUIRE(nominal != Null<Real>(), "nominal null or not set");
        QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(),
                   "number of fixed start dates different from "
                   "number of fixed payment dates");
        QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(),
                   "number of fixed payment dates different from "
                   "number of fixed coupon amounts");

    }

    void SingleFixedLeg::results::reset() {
        Swap::results::reset();
        fairRate = Null<Rate>();
    }

    std::ostream& operator<<(std::ostream& out, SingleFixedLeg::Type t) {
        switch (t) {
          case SingleFixedLeg::Payer:
            return out << "Payer";
          case SingleFixedLeg::Receiver:
            return out << "Receiver";
          default:
            QL_FAIL("unknown SingleFixedLeg::Type(" << Integer(t) << ")");
        }
    }

}
